package App;

import javafx.animation.AnimationTimer;
import javafx.application.Platform;
import javafx.geometry.Rectangle2D;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.stage.Screen;
import javafx.stage.Stage;

import java.io.*;
import java.util.Stack;

public class Controller {
    private static final int AXISOFFSETY = 200;
    private static final int EXTRAFINDOFFSETY = 50;
    private static final double SCALEC = 1;
    private static final int SOBELTHREH = 40;
    private static final int SOBELTHREL = 20;
    static final double PROJECTION = 1.76;
    public ImageView imgViewProj;
    public Label lbADBConnectHint;
    public ScrollPane spADBShowArea;
    public VBox vBoxADB;
    public Button btnADBRun;
    public Button btnADBConn;
    public ImageView imgViewGray;
    public ImageView imgViewOri;
    public VBox vbMessageBox;
    public CheckBox cbShowPic;
    public Button btnAbout;
    public Hyperlink linkProject;
    private WritableImage imgSrc;
    private WritableImage imgDes;
    private WritableImage imgOri;
    private int[][] grayFake = new int[540][960];
    private int[][] gray = new int[540][960];
    private int[][] graysobelh = new int[540][960];
    private int[][] graysobelv = new int[540][960];
    private int[][] graysobel = new int[540][960];
    private int[][] graysobelhf = new int[540][960];
    private int[][] graysobelvf = new int[540][960];
    private int[][] graysobelf = new int[540][960];

    private byte[][] edge = new byte[540][960];
    private byte[][] edgeAfterProj;
    public ImageView imgViewSrc;
    public ImageView imgViewDes;
    public boolean bWaiting = true;
    public boolean bSuccess = false;
    public Point center = null;
    public Point centerProj;
    private Point findTop;
    private Point findBottom;
    public Point findCenter;
    private Point findLeft;
    private Point findRight;
    private boolean bIsRect;
    private Stage mainStage;

    private Thread threadADB;
    private boolean bADBRunning;
    public String PicDir = "";
    public int PicCount;
    public double timeScale = 365;
    private double vbMessageBoxHeight;
    private final double  windowMinWidth=270;
    private final double windowMinHeight=500;
    private final double  windowMaxWidth=1130;
    private final double windowMaxHeight=1020;
    private final double vbMessageBoxMinHeight=300;


    void Init(Stage main,Parent parent) {
        mainStage = main;
        vbMessageBoxHeight=vbMessageBox.getPrefHeight();

        vbMessageBox.setPrefHeight(vbMessageBoxMinHeight);
        Scene scene = new Scene(parent, windowMinWidth, windowMinHeight);
        main.setScene(scene);
        FitScreen(windowMinWidth,windowMinHeight);
        mainStage.setResizable(false);
        AnimationTimer timer=new AnimationTimer() {
            @Override
            public void handle(long now) {
                spADBShowArea.setVvalue(1);
            }
        };
        timer.start();
        btnADBConn.setOnAction(event -> {

            try {
                Process processADB = Runtime.getRuntime().exec( "adb shell");
                InputStream inputStream = processADB.getInputStream();
                new Thread(new Runnable() {
                    byte[] cache = new byte[1024];

                    public void run() {
                        try {
                            int i;
                            while ((i = inputStream.read(cache)) != -1) {
                                String s = new String(cache, 0, i);
                                if (s.endsWith(":/ $ ")) {
                                    Platform.runLater(() -> {
                                        lbADBConnectHint.setText("已连接");
                                        btnADBRun.setDisable(false);
                                        btnADBRun.requestFocus();
                                    });
                                }

                            }
                        } catch (IOException ignored) {
                        }
                    }
                }).start();
            } catch (Exception e) {
                if (e instanceof IOException) {
                    AddMessage("Error ADB!!!");
                }
                e.printStackTrace();
            }
        });
        cbShowPic.selectedProperty().addListener((observable, oldValue, newValue) -> {

            double width=0;double height=0;
            if(newValue){
                vbMessageBox.setPrefHeight(vbMessageBoxHeight);
                width=windowMaxWidth;
                height=windowMaxHeight;



            }else{
                vbMessageBox.setPrefHeight(vbMessageBoxMinHeight);
                width=windowMinWidth+20;
                height=windowMinHeight+40;
            }
            main.setWidth(width);
            main.setHeight(height);
            FitScreen(width,height);


        });
        btnADBRun.setOnAction(event -> {
            if(!bADBRunning) {
                if (threadADB != null && threadADB.isAlive()) {
                    threadADB.interrupt();
                }
                threadADB = new Thread(new ADBThread(this));
                threadADB.start();
                bADBRunning=true;
            }


        });
        mainStage.setOnCloseRequest(event -> {
            System.exit(0);
        });
        btnAbout.setOnAction(event -> {
            Alert alert=new Alert(Alert.AlertType.INFORMATION);
            alert.setTitle("关于 跳一跳助手");
            alert.setHeaderText("JavaFX版跳一跳助手");
            alert.setContentText("作者：zhaoziqiu1995\n注意：\n1.严禁用于商业用途！！！\n2.仅供学习交流使用。\n3.可以通过任意形式对代码进行修改和发布，但由此产生的后果由修改者承担，作者概不负责。");
            alert.showAndWait();

        });
        spADBShowArea.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
        linkProject.setOnAction(event ->{
            try {
                Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler https://gitee.com/zhaoziqiu1995/JumpAssistJFX");
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
        btnADBConn.requestFocus();


    }

    private void FitScreen(double width, double height) {
        Screen screen=Screen.getPrimary();
        Rectangle2D bounds=screen.getBounds();
        double widthb=bounds.getWidth();
        double heightb=bounds.getHeight();
        mainStage.setX((widthb-width)/2);
        mainStage.setY((heightb-height)/2);
    }

    void Procedure(File file) throws FileNotFoundException {
        FileInputStream f = new FileInputStream(file);
        Image temp = new Image(f);
        imgSrc = new WritableImage(540, 960);
        imgDes = new WritableImage(540, 960);
        imgOri = new WritableImage(540, 960);
        WritableImage imgGray = new WritableImage(540, 960);
        for (int i = 0; i < imgSrc.getWidth(); i++)
            for (int j = 0; j < imgSrc.getHeight(); j++) {
                Color c = temp.getPixelReader().getColor(i * 2, j * 2);
                HSV hsv = new RGB((int) (c.getRed() * 256), (int) (c.getGreen() * 256), (int) (c.getBlue() * 256)).convertToHSV();
                grayFake[i][j] = (int) (hsv.S() * 255);
                gray[i][j] = (int) (c.getBrightness() * 255);
                imgOri.getPixelWriter().setColor(i, j, temp.getPixelReader().getColor(i * 2, j * 2));
                imgGray.getPixelWriter().setColor(i, j, Color.grayRgb(gray[i][j]));
            }
        gray = Smooth(gray);
        grayFake = Smooth(grayFake);
        for (int i = 1; i < imgSrc.getWidth() - 1; i++)
            for (int j = 1; j < imgSrc.getHeight() - 1; j++) {
                graysobelh[i][j] = -gray[i - 1][j - 1] - 2 * gray[i - 1][j] - gray[i - 1][j + 1]
                        + gray[i + 1][j - 1] + 2 * gray[i + 1][j] + gray[i + 1][j + 1];
                graysobelv[i][j] = -gray[i - 1][j - 1] - 2 * gray[i][j - 1] - gray[i + 1][j - 1]
                        + gray[i - 1][j + 1] + 2 * gray[i][j + 1] + gray[i + 1][j + 1];

                graysobelhf[i][j] = -grayFake[i - 1][j - 1] - 2 * grayFake[i - 1][j] - grayFake[i - 1][j + 1]
                        + grayFake[i + 1][j - 1] + 2 * grayFake[i + 1][j] + grayFake[i + 1][j + 1];
                graysobelvf[i][j] = -grayFake[i - 1][j - 1] - 2 * grayFake[i][j - 1] - grayFake[i + 1][j - 1]
                        + grayFake[i - 1][j + 1] + 2 * grayFake[i][j + 1] + grayFake[i + 1][j + 1];
            }
        Sobel();
        Fresh();
        WritableImage imgProj = Projection();
        imgViewOri.setImage(imgOri);
        imgViewProj.setImage(imgProj);
        imgViewGray.setImage(imgGray);
        bWaiting = false;
        bSuccess=true;
    }

    void AddMessage(String s) {
        Platform.runLater(() -> {
            Text text = new Text(s);
            StackPane pane=new StackPane();
            pane.getChildren().add(text);
            vBoxADB.getChildren().add(pane);
            spADBShowArea.setVvalue(vBoxADB.getHeight());
        });

    }

    private void Sobel() {
        for (int i = 0; i < 540; i++)
            for (int j = 0; j < 960; j++) {
                graysobel[i][j] = (int) Math.sqrt(graysobelh[i][j] * graysobelh[i][j] + graysobelv[i][j] * graysobelv[i][j]);
                graysobelf[i][j] = (int) Math.sqrt(graysobelhf[i][j] * graysobelhf[i][j] + graysobelvf[i][j] * graysobelvf[i][j]);

            }
    }

    private int[][] Smooth(int[][] gray) {
        int[] ss = new int[]{1, 2, 1, 2, 4, 2, 1, 2, 1};
        int[][] ret = new int[540][960];
        for (int i = 1; i < 540 - 1; i++)
            for (int j = 1; j < 960 - 1; j++) {
                int temp = (ss[0] * gray[i - 1][j - 1] +
                        ss[1] * gray[i][j - 1] +
                        ss[2] * gray[i + 1][j - 1] +
                        ss[3] * gray[i - 1][j] +
                        ss[4] * gray[i][j] +
                        ss[5] * gray[i + 1][j] +
                        ss[6] * gray[i - 1][j + 1] +
                        ss[7] * gray[i][j + 1] +
                        ss[8] * gray[i - 1][j + 1]) / 16;
                if (temp < 0) temp = 0;
                if (temp > 255) temp = 255;
                ret[i][j] = temp;

            }
        return ret;
    }

    private void Fresh() {
        imgDes = new WritableImage(540, 960);
        if (imgSrc != null) {
            for (int i = 0; i < imgSrc.getWidth(); i++)
                for (int j = 0; j < imgSrc.getHeight(); j++) {
                    if (Math.abs(graysobel[i][j]) > SOBELTHREH || Math.abs(graysobelf[i][j]) > SOBELTHREH) {
                        edge[i][j] = 1;
                    } else {
                        edge[i][j] = 0;
                    }
                    if (edge[i][j] == 0 && (Math.abs(graysobel[i][j]) > SOBELTHREL || Math.abs(graysobelf[i][j]) > SOBELTHREL)) {
                        edge[i][j] = 2;
                    }



                }



            Canny();
        }


        if (imgSrc != null) {
            for (int i = 0; i < 540; i++)
                for (int j = 0; j < 960; j++) {
                    int temp = graysobel[i][j];
                    if (temp < 0) temp = 0;
                    if (temp > 255) temp = 255;
                    imgDes.getPixelWriter().setColor(i, j, Color.rgb(temp, temp, temp));
                }
            imgDes = FindSelf(imgDes);
            imgViewDes.setImage(imgDes);
            for (int i = 0; i < imgSrc.getWidth(); i++)
                for (int j = 0; j < imgSrc.getHeight(); j++) {
                    switch (edge[i][j]) {
                        case 0:
                            imgSrc.getPixelWriter().setColor(i, j, Color.BLACK);
                            break;
                        case 1:
                            imgSrc.getPixelWriter().setColor(i, j, Color.RED);
                            break;
                    }
                    if (j == 390 || j == 520 || j == AXISOFFSETY) imgSrc.getPixelWriter().setColor(i, j, Color.ORANGE);
                }
            imgViewSrc.setImage(imgSrc);


        }

        if (center != null) {

            for (int k = center.X() - 3; k < center.X() + 3; k++)
                for (int l = center.Y() - 3; l < center.Y() + 3; l++)
                    imgSrc.getPixelWriter().setColor(k, l, Color.BLUE);

        }
        imgViewSrc.setImage(imgSrc);
    }

    private HSV GetHSVByEdge(int i, int j) {
        Color c= imgOri.getPixelReader().getColor(i,j);
        RGB rgb=new RGB((int)(c.getRed()*255),(int)(c.getGreen()*255),(int)(c.getBlue()*255));
        return rgb.convertToHSV();
    }

    private WritableImage Projection() {
        double proj = PROJECTION;
        byte[][] temp = new byte[540][1920];
        edgeAfterProj = new byte[540][1920];
        WritableImage image = new WritableImage(540, 1920);
        for (int i = 0; i < 960; i++) {
            for (int j = 0; j < 540; j++) {
                int t = (int) ((i - 480) * proj + 960);
                if (t > 0 && t < 1920)
                    temp[j][t] = edge[j][i];
            }
        }
        for (int i = 0; i < 540; i++) {
            for (int j = 1; j < 1919; j++) {
                edgeAfterProj[i][j]=temp[i][j];
                if(temp[i][j-1]==1&&temp[i][j+1]==1)
                    edgeAfterProj[i][j]=1;


            }
        }
        if (center != null)
            centerProj = new Point(center.X(), (int) ((center.Y() - 480) * proj + 960));
        if (centerProj != null)
            for (int i = 0; i < 540; i++)
                for (int j = 0; j < 1920; j++) {
                    if (edgeAfterProj[i][j] == 1) {
                        image.getPixelWriter().setColor(i, j, Color.RED);
                    } else {
                        image.getPixelWriter().setColor(i, j, Color.BLACK);
                    }
                    if (i == centerProj.X() && j == centerProj.Y()) {
                        for (int k = i - 3; k < i + 3; k++)
                            for (int l = j - 3; l < j + 3; l++)
                                image.getPixelWriter().setColor(k, l, Color.GOLD);
                    }
                    if (j == 450 || j == centerProj.Y() + EXTRAFINDOFFSETY)
                        image.getPixelWriter().setColor(i, j, Color.YELLOW);
                }
        if (center != null && centerProj != null)
            try {
                Find(image);
            }catch (Exception e) {
            e.printStackTrace();
            }
        if (bIsRect) GetStage().setTitle("Rect");
        else GetStage().setTitle("Circle");
        return image;
    }

    private Stage GetStage() {
        return mainStage;
    }

    private void Find(WritableImage image) {
        bIsRect = false;
        Point topPre = null;
        int topPreX = 0;
        findTop = null;
        findBottom = null;
        int start, end;
        int r1 = 0, r2 = 0;
        boolean bDir = centerProj.X() < 270;
        for (int i = 450; i < centerProj.Y() + EXTRAFINDOFFSETY; i++) {
            boolean flag = false;
            topPreX = 0;
            int count = 0;
            if (bDir) {
                for (int j = 520; j > 290; j--) {
                    if (edgeAfterProj[j][i] == 1 && !flag) {
                        flag = true;
                        start = j;
                    }
                    if ((edgeAfterProj[j][i] == 0 && flag)) {
                        flag = false;
                        end = j;
                        topPreX /= count;
                        break;
                    }
                    if (flag) {
                        topPreX += j;
                        count++;
                    }


                }
                if (flag) {
                    flag = false;
                    topPreX /= count;
                }
            } else {
                for (int j = 20; j < 250; j++) {
                    if (edgeAfterProj[j][i] == 1 && !flag) {
                        flag = true;
                        start = j;
                    }
                    if (edgeAfterProj[j][i] == 0 && flag) {
                        flag = false;
                        end = j;
                        topPreX /= count;
                        break;
                    }
                    if (flag) {
                        topPreX += j;
                        count++;
                    }
                }
                if (flag) {
                    flag = false;
                    topPreX /= count;
                }
            }
            if (topPreX != 0) {
                if (Math.abs(topPreX - centerProj.X()) > 50) {
                    topPre = new Point(topPreX, i);
                    break;
                }
                topPreX = 0;
            }

        }

        if (topPreX!=0) {
            findTop = topPre;

        }
        if (findTop != null) {
            int x = findTop.X();
            int y = findTop.Y();
            bDir = centerProj.X() < 270;
            int s = 12;
            boolean flag = false;
            while (true) {
                for (int i = 0; i < 540; i++) {
                    if (edgeAfterProj[i][y + s] == 1) {
                        flag = true;
                        break;
                    }
                }
                if (!flag)
                    s++;
                else
                    break;
            }
            if (!bDir) {
                int t = x;
                while (t > 30) {
                    boolean b = false;

                    for (int i = t; i > t - 30; i--) {
                        if (edgeAfterProj[i][y + s] == 1) {
                            b = true;
                            break;
                        }
                    }
                    if (!b) break;
                    else t--;
                }
                if (t != 30) {
                    int dis = x - t;
                    int r = (int) ((s * s + dis * dis) / (2 * s) * SCALEC);
                    if (r > 20) {
                        bIsRect=false;
                        findCenter = new Point(x, y + r);
                    }else bIsRect=true;
                }
            } else {
                int t = x;
                while (t < 510) {
                    boolean b = false;
                    for (int i = t; i < t + 30; i++) {
                        if (edgeAfterProj[i][y + s] == 1) {
                            b = true;
                            break;
                        }
                    }
                    if (!b) break;
                    else t++;
                }
                if (t != 510) {
                    int dis = t - x;
                    int r = (int) ((s * s + dis * dis) / (2 * s) * SCALEC);
                    if (r > 20) {
                        bIsRect=false;
                        findCenter = new Point(x, y + r);
                    }else bIsRect=true;
                }

            }
        }
        if(bIsRect){
            if(bDir){
                int x=findTop.X();
                int y=findTop.Y();
                int jj=y;
                for(int j=findTop.Y();j<centerProj.Y()+EXTRAFINDOFFSETY-60;j++) {
                    boolean b=true;
                    for (int k = j; k < j + 60; k++) {
                        if (edgeAfterProj[findTop.X()][k] == 0) {
                            b=false;
                            break;
                        }
                    }
                    if(b){
                        jj=findTop.Y()+(j-findTop.Y())/2;
                        findBottom=new Point(x,2*(jj-findTop.Y())+findTop.Y());
                        break;
                    }
                }
                if(findBottom==null){
                    for(int i=x+10;i<530;i++) {
                        boolean flag = false;
                        for (int j = y; j < y + 20; j++) {
                            if (edgeAfterProj[i][j] == 1) {
                                flag = true;
                                jj=j;
                                break;
                            }

                        }
                        int sum=0;
                        for(int j=y;j<y+100;j++){
                            if(edgeAfterProj[i][j]==1) {
                                sum++;
                            }
                        }
                        if(sum>95){
                            flag=false;
                        }

                        if(flag)y=jj;
                        else{
                            findBottom=new Point(x,2*(jj-findTop.Y())+findTop.Y());
                            break;
                        }
                    }

                }
                if(findBottom==null) {
                    findBottom=new Point(x,2*(jj-findTop.Y())+findTop.Y());
                }



            }else{
                int x=findTop.X();
                int y=findTop.Y();
                int jj=y;
                for(int j=findTop.Y();j<centerProj.Y()+EXTRAFINDOFFSETY-60;j++) {
                    boolean b=true;
                    for (int k = j; k < j + 60; k++) {
                        if (edgeAfterProj[findTop.X()][k] == 0) {
                            b=false;
                            break;
                        }
                    }
                    if(b){
                        jj=findTop.Y()+(j-findTop.Y())/2;
                        findBottom = new Point(x, 2 * (jj - findTop.Y()) + findTop.Y());
                        break;
                    }
                }
                if(findBottom==null){
                    for(int i=x-10;i>10;i-=2) {
                        boolean flag = false;
                        for (int j = y; j < y + 20; j++) {
                            if (edgeAfterProj[i][j] == 1) {
                                flag = true;
                                jj=j;
                                break;
                            }

                        }
                        int sum=0;
                        for(int j=y;j<y+100;j++){
                            if(edgeAfterProj[i][j]==1) {
                                sum++;
                            }
                        }
                        if(sum>95){
                            flag=false;
                        }

                        if(flag)y=jj;
                        else{
                            findBottom=new Point(x,2*(jj-findTop.Y())+findTop.Y());
                            break;
                        }
                    }

                }
                if(findBottom==null) {
                    findBottom=new Point(x,2*(jj-findTop.Y())+findTop.Y());


                }
            }

        }


        if(bIsRect) {
            if (findTop != null&&findBottom!=null) {
                findCenter = new Point((findTop.X() + findBottom.X()) / 2, (findTop.Y() + findBottom.Y()) / 2);
                if ((Dis2(findTop, findCenter) > 12000 && Dis2(findCenter, centerProj) < 1200) || findCenter.Y() > centerProj.Y()) {
                    findCenter = null;
                    findTop = null;
                    findBottom = null;
                    findLeft = null;
                    findRight = null;

                }
                if (findCenter != null) {
                    int dis = findCenter.Y() - findTop.Y();
                    findLeft = new Point(findCenter.X() - dis, findCenter.Y());
                    findRight = new Point(findCenter.X() + dis, findCenter.Y());
                    //DrawRectangle(image);
                    DrawPoints(image);
                    DrawLine(centerProj, findCenter, image);
                }
            }
        }else{
            if (findTop != null&&findCenter!=null) {
                int dis = findCenter.Y() - findTop.Y();
                findLeft = new Point(findCenter.X() - dis, findCenter.Y());
                findRight = new Point(findCenter.X() + dis, findCenter.Y());
                findBottom=new Point(findCenter.X(),findCenter.Y()+dis);
                //DrawCircle(image);
                DrawPoints(image);
                DrawLine(centerProj, findCenter, image);
            }
        }
        boolean bSuccess = false;
        if(findCenter!=null) bSuccess =true;

    }

    private void DrawPoints(WritableImage image) {
        DrawPoint(findTop, image);
        DrawPoint(findBottom, image);
        DrawCenterPoint(findCenter, image);
        DrawPoint(findLeft, image);
        DrawPoint(findRight, image);
    }

    private void DrawCircle(WritableImage image) {
        int r=findTop.Y()-findCenter.Y();
        int x=findCenter.X();
        int y=findCenter.Y();
        int top = findTop.Y();
        int bottom = findBottom.Y();
        int left = findLeft.X();
        int right = findRight.X();
        for(int i=left;i<right;i++)
            for(int j=top;j<bottom;j++){
            if((i-x)*(i-x)+(j-y)*(j-y)<r*r){
                image.getPixelWriter().setColor(i,j,Color.CYAN);
            }
        }


    }

    private int Dis2(Point p1, Point p2) {
        return (p1.X()-p2.X())*(p1.X()-p2.X())
                +(p1.Y()-p2.Y())*(p1.Y()-p2.Y());

    }

    private void DrawLine(Point p1, Point p2, WritableImage image) {
        double k = CalcK(p1, p2);
        double b = CalcB(p1, p2);
        int min = Math.min(p1.X(), p2.X());
        int max = p1.X() + p2.X() - min;
        for (int i = min; i < max; i++) {
            image.getPixelWriter().setColor(i, (int) (k * i + b - 1), Color.GOLD);
            image.getPixelWriter().setColor(i, (int) (k * i + b), Color.GOLD);
            image.getPixelWriter().setColor(i, (int) (k * i + b + 1), Color.GOLD);
        }
    }

    private void DrawCenterPoint(Point findCenter, WritableImage image) {
        for (int i = findCenter.X() - 3; i <= findCenter.X() + 3; i++)
            for (int j = findCenter.Y() - 3; j <= findCenter.Y() + 3; j++) {
                if(i>0&&i<540&&j>0&&j<1920)
                image.getPixelWriter().setColor(i, j, Color.GOLD);
            }
    }

    private void DrawRectangle(WritableImage image) {
        int top = findTop.Y();
        int bottom = findBottom.Y();
        int left = findLeft.X();
        int right = findRight.X();
        double k1, k2, k3, k4, b1, b2, b3, b4;
        k1 = CalcK(findTop, findLeft);
        b1 = CalcB(findTop, findLeft);
        k2 = CalcK(findTop, findRight);
        b2 = CalcB(findTop, findRight);
        k3 = CalcK(findBottom, findLeft);
        b3 = CalcB(findBottom, findLeft);
        k4 = CalcK(findBottom, findRight);
        b4 = CalcB(findBottom, findRight);
        for (int i = left; i < right; i++)
            for (int j = top; j < bottom; j++) {
                if (k1 * i + b1 - j < 0 && k2 * i + b2 - j < 0 && k3 * i + b3 - j > 0 && k4 * i + b4 - j > 0) {
                    if(i>0&&i<540&&j>0&&j<1920)
                    image.getPixelWriter().setColor(i, j, Color.CYAN);
                }
            }

    }

    private double CalcB(Point p1, Point p2) {
        return p1.Y() - CalcK(p1, p2) * p1.X();
    }

    private double CalcK(Point p1, Point p2) {
        return ((double) p1.Y() - p2.Y()) / (p1.X() - p2.X());

    }

    private void DrawPoint(Point point, WritableImage image) {
        for (int i = point.X() - 2; i <= point.X() + 2; i++)
            for (int j = point.Y() - 2; j <= point.Y() + 2; j++) {
                if(i>0&&i<540&&j>0&&j<1920)
                image.getPixelWriter().setColor(i, j, Color.GREEN);
            }
    }

    private double sin(double degree) {
        return Math.sin(degree * Math.PI / 180);
    }

    private double cos(double degree) {
        return Math.cos(degree * Math.PI / 180);
    }


    private int GrayValue(int r, int g, int b) {
        return (int) (0.299 * r + 0.587 * g + 0.114 * b);
    }

    private WritableImage FindSelf(WritableImage image) {
        byte[][] self = new byte[540][960];
        if (imgOri != null) {
            center = null;
            for (int i = 15; i < 525; i++) {
                for (int j = 390; j < 520; j++) {

                    HSV hsv=GetHSVByEdge(i,j);
                    if(hsv.H()>220&&hsv.H()<260&&hsv.S()>0.2&&hsv.S()<0.4&&hsv.V()<0.55)
                        self[i][j]=1;

                }
            }
            for (int i = 15; i < 525; i++) {
                for (int j = 390; j < 520; j++) {
                    int sum = 0;

                    if (self[i][j] == 1) {
                        if (edge[i][j] == 1) edge[i][j] = 0;
                    }
                }
            }
            int miny=Integer.MAX_VALUE;
            for (int i = 15; i < 525; i++) {
                for (int j = 390; j < 520; j++) {
                    int sum = 0;

                    if (self[i][j] == 1) {
                        for (int l = i - 10; l < i + 10; l++)
                            for (int m = j - 10; m < j + 10; m++) {
                                if ((l - i) * (l - i) + (m - j) * (m - j) < 100 && self[l][m] == 1) sum++;
                            }
                        if (sum > 200) {
                            if(j<miny) {
                                miny=j;
                                center = new Point(i + 8, j + 86);
                            }
                        }
                    }
                }
                if (center != null) break;
            }
            for (int i = 0; i < imgSrc.getWidth(); i++)
                for (int j = 0; j < imgSrc.getHeight(); j++) {
                    switch (self[i][j]) {
                        case 0:
                            //image.getPixelWriter().setColor(i, j, Color.BLACK);
                            break;
                        case 1:
                            image.getPixelWriter().setColor(i, j, Color.PURPLE);
                            break;
                    }
                }

            return image;
        }else
        return null;
    }

    private void Canny() {
        byte[][] hint=new byte[540][960];
        boolean bConnect=false;
        Stack<Point> points=new Stack<>();
        for(int i = 2; i< imgSrc.getWidth()-2; i++)
            for(int j = 2; j< imgSrc.getHeight()-2; j++){
                if(hint[i][j]==0) {
                    hint[i][j] = 1;
                    if (edge[i][j] == 1) {
                        points.push(new Point(i, j));
                        while (!points.empty()) {
                            Point p = points.pop();
                            for (int a = p.X() - 2; a <= p.X() + 2; a++)
                                for (int b = p.Y() - 2; b <= p.Y() + 2; b++) {
                                    if (a>=0&&b>=0&&a<imgSrc.getWidth()&&b<imgSrc.getHeight()&&a != i && b != j && hint[a][b] == 0) {
                                        hint[a][b] = 1;
                                        if (edge[a][b] == 2) edge[a][b] = 1;
                                        if (edge[a][b] != 0) points.push(new Point(a, b));

                                    }
                                }
                        }


                    }
                }

            }
        for(int i = 0; i< imgSrc.getWidth(); i++)
            for(int j = 0; j< imgSrc.getHeight(); j++) {
                if(edge[i][j]!=1){
                    edge[i][j]=0;
                }

            }
    }

}
class Point{
    private int mx;
    private int my;
    Point(int x,int y){
        mx=x;
        my=y;
    }
    int X(){
        return mx;
    }
    int Y(){
        return my;
    }

    @Override
    public String toString() {
        return String.format("X:%d Y:%d",mx,my);
    }
}

class RGB{
    private static double max3(double a, double b, double c){
        return (a>b?(a>c?a:(b>c?b:c)):(b>c?b:c));
    }
    private static double min3(double a, double b, double c){
        return (a<b?(a<c?a:(b<c?b:c)):(b<c?b:c));
    }
    private int mR,mG,mB;
    RGB(int r,int g,int b){
        mR=r;mG=g;mB=b;
    }
    int R(){return mR;}
    int G(){return mG;}
    int B(){return mB;}
    void setR(int v){mR=v;}
    void setG(int v){mG=v;}
    void setB(int v){mB=v;}
    HSV convertToHSV(){
        double max,min;
        double H=0,S=0,V=0;
        max=max3(mR,mG,mB);
        min=min3(mR,mG,mB);
        if (mR == max)  H = (mG-mB)/(max-min);
        if (mG == max) H = 2 + (mB-mR)/(max-min);
        if (mB == max) H = 4 + (mR-mG)/(max-min);
        H = H * 60;
        if (H < 0) H = H + 360;
        V=max3(mR,mG,mB)/256.0;
        S=(max-min)/max;
        return new HSV(H,S,V);
    }
}
class HSV{
    private double mH,mS,mV;
    HSV(double h,double s,double v){
        mH=h;mS=s;mV=v;
    }
    double H(){return mH;}
    double S(){return mS;}
    double V(){return mV;}
    void setH(double v){mH=v;}
    void setS(double v){mS=v;}
    void setV(double v){mV=v;}
}
class ADBThread implements Runnable {

    private Controller mController;
    private String PicDir;

    ADBThread(Controller c) {
        mController = c;
        PicDir = mController.PicDir;

    }

    @Override
    public void run() {
        while (!Thread.interrupted()) {
            try {
                String fetchScreen = "adb shell /system/bin/screencap -p /sdcard/screenshot.png";
                String pullScreen;
                if (!PicDir.equals("")) {
                    pullScreen = String.format("adb pull /sdcard/screenshot.png %s%d.png", PicDir, ++mController.PicCount);
                } else
                    pullScreen = "adb pull /sdcard/screenshot.png default.png";

                AddMessage("----------Fetch screen----------");
                Command(fetchScreen);
                if (!PicDir.equals(""))
                    AddMessage("----------Pull screen:" + mController.PicCount + "---------");
                else
                    AddMessage("-----------Pull screen----------");
                Command(pullScreen);
                String path = "";
                if (!PicDir.equals(""))
                    path = PicDir + (mController.PicCount) + ".png";
                else
                    path = "default.png";
                String finalPath = path;
                AddMessage("-----------Processing----------");
                mController.bWaiting=true;
                Platform.runLater(() -> {
                    try {

                        mController.bSuccess=false;
                        mController.Procedure(new File(finalPath));
                    } catch (FileNotFoundException e) {
                        mController.bWaiting=false;
                        mController.bSuccess=false;
                        e.printStackTrace();
                    }
                });

                while (mController.bWaiting) {
                    Thread.sleep(1);
                }
                if (mController.bSuccess) {
                    Point center1 = mController.center;
                    Point temp = mController.findCenter;
                    double proj = Controller.PROJECTION;
                    int y = 480 + (int) ((temp.Y() - 960) / proj);
                    Point center2 = new Point(temp.X(), y);
                    double dis = Math.sqrt((center1.X() - center2.X()) * (center1.X() - center2.X()) +
                            (center1.Y() - center2.Y()) * (center1.Y() - center2.Y()));
                    int ms = (int) (dis * 1000 / mController.timeScale);
                    AddMessage("--------SUCCESS JUMP!!!-------");
                    AddMessage("-----------DIS:" + String.format("%.2f", dis) + "----------");
                    AddMessage("----------TIME:" + ms + "ms---------");
                    int startx = 600 + (int) (Math.random() * 60 - 30);
                    int starty = 1600 + (int) (Math.random() * 60 - 30);
                    int endx = startx + (int) (Math.random() * 20 - 10);
                    int endy = starty + (int) (Math.random() * 20 - 10);
                    Thread.sleep((int) (Math.random() * 400));
                    String jumpCommand = "adb shell input swipe " + startx + " " + starty + " " + endx + " " + endy + " " + ms;
                    Command(jumpCommand);
                    Thread.sleep(ms + 1000);
                } else {
                    AddMessage("----------FAILURE!!!---------");
                    int startx = 600 + (int) (Math.random() * 60 - 30);
                    int starty = 1600 + (int) (Math.random() * 60 - 30);
                    String jumpCommand = "adb shell input tap " + startx + " " + starty;
                    Command(jumpCommand);
                }


            } catch (IOException | InterruptedException ignored) {

            }


        }

    }

    private void AddMessage(String s) {
        mController.AddMessage(s);
    }


    private void Command(String cmd) throws IOException, InterruptedException {
        Process p = Runtime.getRuntime().exec(cmd);
        p.waitFor();
    }

}